function defineReactive(obj, key, val) {
  // 递归  如果val 还是对象的话 进行遍历响应式
  observe(val)

  // 创建一个Dep 和 当前的可以 一一对应

  const dep = new Dep()
  //对传入obj 进行访问拦截
  // 在每次执行defineReactive 时候，其实形成了一个闭包，因为在内部保留了一个内部作用域的变量 就是value
  Object.defineProperty(obj, key, {
    get() {
      console.log('get', key)

      // 核心的依赖手机过程 发生在get 中
      Dep.target && dep.addDep(Dep.target)

      return val
    },
    set(newVal) {
      if (newVal !== val) {
        console.log('set::' + key + ':' + newVal)
        // 如果newVal 是对象，应该做响应化的处理
        observe(newVal)
        val = newVal

        // 执行更新函数遍历watchers
        // watchers.forEach(w => {
        //     w.update()
        // })
        dep.notify()
      }
    },
  })
}

function observe(obj) {
  if (typeof obj !== 'object' || obj == null) {
    // 希望传入的是obj
    return
  }
  // 遍历 相当于所有的可以 都定义了响应式
  // Object.keys(obj).forEach(key => {
  //     defineReactive(obj, key, obj[key])
  // })
  //此处不再是直接循环遍历，而是要引入一个新的角色Observer 一个主要作用是分析数据是对象还是数组。
  // 创建Observer 的实例
  new Observer(obj)
}
// 代理函数，方便用户直接访问$data中的数据
function proxy(vm, sourceKey) {
  //框架实例  以及要代理的属性可以
  Object.keys(vm[sourceKey]).forEach((key) => {
    Object.defineProperty(vm, key, {
      get() {
        return vm[sourceKey][key]
      },
      set(newVal) {
        vm[sourceKey][key] = newVal
      },
    })
  })
}
// 创建zvue的构造函数
class ZVue {
  constructor(options) {
    // 保存选项
    this.$options = options
    this.$data = options.data

    // 响应化处理
    observe(this.$data)

    // 代理
    proxy(this, '$data')

    // 创建编译器的实例
    new Compiler(options.el, this)
  }
}
// 根据对象类型决定如何做响应化
class Observer {
  constructor(value) {
    this.value = value
    // 判断其类型
    if (typeof value === 'object') {
      this.walk(value)
    }
  }
  //对象数据的响应化
  walk(obj) {
    Object.keys(obj).forEach((key) => {
      defineReactive(obj, key, obj[key])
    })
  }
  // 数组数据的响应化 待补充
}

// 创建观察者:保存更新函数，值发生变化 调用更新函数
// 在没有Dep 的情况下 先自己把每一个watcher 保存起来
// const watchers = []

class Watcher {
  constructor(vm, key, updateFn) {
    this.vm = vm

    this.key = key

    this.updateFn = updateFn

    // watchers.push(this)

    // Dep.target 静态属性上设置为当前watcher 实例
    Dep.target = this
    //
    this.vm[this.key] // 读取触发get 因此可以在get 中 将其target 追加进去
    Dep.target = null // 收集完后置空
  }
  update() {
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}

//Dep:依赖，管理某个key 相关所有Watcher实例
class Dep {
  constructor() {
    this.deps = []
  }

  addDep(dep) {
    // dep 就是watcher 实例
    this.deps.push(dep)
  }

  notify() {
    this.deps.forEach((dep) => {
      dep.update()
    })
  }
}
